﻿#define verbose
using Plexi.Science;
using Plexi.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Plexi
{
	public class RockPart : PartModule
	{
		private double _lat, _lon, _elev;
		private Vector3d _pos;
		private CelestialBody _body;
		private float _accumulator;
		public RealRockInfo Rri { get; set; }

		[KSPField(guiActive = false, isPersistant = true)]
		public string RockInfoString;

		[KSPEvent(active = true, externalToEVAOnly = true, guiActive = true, guiActiveUnfocused = true, guiName = "Examine", unfocusedRange = 3.5f)]
		public void ContextMenuInfo()
		{
			SurfaceRockSpawner.ExaminedByScientist = FlightGlobals.ActiveVessel.GetVesselCrew().First().experienceTrait.Config.Name.ToLower().Equals("scientist");
			SurfaceRockSpawner.SelectedRockData = Rri.Data;
		}

		public override void OnUpdate()
		{
			_accumulator += Time.deltaTime;
			if (_accumulator >= 5.0f)
			{
				_accumulator = 0.0f;
				if (vessel == null)
					return;
				_body = vessel.mainBody;
				if (_body == null)
					return;
				try
				{
					_pos = vessel.GetWorldPos3D();
				}
				catch (NullReferenceException)
				{
					return;
				}
				_lat = _body.GetLatitude(_pos);
				_lon = _body.GetLongitudeNormalized(_pos);
				_elev = KspTools.GetElevation(_body, _lat, _lon);
				if (_elev < 0)
					_elev = 0;
				if ((vessel.altitude - _elev < 0))
				{
#if DEBUG
					Log.Add("Killing rock vessel because it's underground.");
#endif
					vessel.Die();
				}
			}
		}

		public override void OnAwake()
		{
			Deserialize();
		}

		public override void OnInitialize()
		{
			Deserialize();
		}

		public override void OnStart(PartModule.StartState state)
		{
			Deserialize();
		}

		public override void OnLoad(ConfigNode node)
		{
			string[] vals = node.GetValues("RockInfo");
			if (vals.Length > 0)
				RockInfoString = vals[0];
		}

		public void Serialize()
		{
			RockInfoString = string.Format("{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}",
				RealRockInfo.CurrentVersion,
				Rri.Data.OriginBody.flightGlobalsIndex.ToString(),
				Rri.Cx,
				Rri.Cy,
				Rri.IndexInCell,
				Rri.Scale,
				Rri.Data.BiomeIndex,
				Rri.Data.Type.Index,
				Rri.TextureIndex);
		}

		public void Deserialize()
		{
			if (Rri != null)
			{
				SurfaceRockSpawner.AddRri(Rri);
				if (string.IsNullOrEmpty(RockInfoString))
					Serialize();
			}
			else if (string.IsNullOrEmpty(RockInfoString))
				return;
			else if (RockInfoString.StartsWith(RealRockInfo.CurrentVersion + '|'))
				DeserializeCurrent();
			else
			{
				Log.Add("Loaded rock vessel has outdated version; destroying...");
				vessel.Die();
				return;
			}
			bool rockAlone = vessel.IsSinglePartVessel(); //check if we're being carried by a kerbal or a magnet/anchor or a grabbing unit
			if (Rri != null)
			{
				if (Rri.Data != null && Rri.Data.Type != null)
				{
					part.partName = Rri.Data.Type.VesselName;
					if (rockAlone)
						vessel.vesselName = part.partName;
					part.mass = Rri.CalculateMass();
				}
			}
			if (rockAlone && vessel.DiscoveryInfo != null)
				vessel.DiscoveryInfo.SetLevel(DiscoveryLevels.Unowned);
		}

		public static RealRockInfo Deserialize(string ser)
		{
			RealRockInfo rri = null;
			int bodyIndex, cx, cy, indexInCell, biomeIndex, typeIndex, textureIndex;
			float scale;
			string[] parts = ser.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
			if (parts.Length != 9)
			{
				Log.Add("ERROR: RockPart Deserialize fail: wrong number of string parts. Loaded string: {0}", ser);
				return null;
			}
			if (int.TryParse(parts[1], out bodyIndex) &&
				int.TryParse(parts[2], out cx) &&
				int.TryParse(parts[3], out cy) &&
				int.TryParse(parts[4], out indexInCell) &&
				float.TryParse(parts[5], out scale) &&
				int.TryParse(parts[6], out biomeIndex) &&
				int.TryParse(parts[7], out typeIndex) &&
				int.TryParse(parts[8], out textureIndex))
			{
				rri = new RealRockInfo()
				{
					BodyIndex = bodyIndex,
					Cx = cx,
					Cy = cy,
					IndexInCell = indexInCell,
					Scale = scale,
					TextureIndex = textureIndex,
					Data = new RockData()
					{
						OriginBody = FlightGlobals.Bodies[bodyIndex],
						BiomeIndex = biomeIndex
					}
				};
				if (typeIndex < 0 || typeIndex >= RockType.AllTypes.Length)
				{
					Log.Add("ERROR: RockPart Deserialize fail: rocktype index {0} is invalid (there are {1} rock types).", typeIndex, RockType.AllTypes.Length);
					return null;
				}
				rri.Data.Type = RockType.AllTypes[typeIndex];
				rri.Data.SetBiomeName();
				rri.CalculateMass();
#if verbose
				Log.Add("RockPart deserialized: {0}, {1}:{2}:{3}, scale: {4}, type: {5}",
					FlightGlobals.Bodies[bodyIndex].bodyName, cx, cy, indexInCell, scale, rri.Data.Type.Name);
#endif
				return rri;
			}
			else
			{
				Log.Add("ERROR: RockPart Deserialize fail: parse error. Loaded string: {0}", ser);
				return null;
			}
		}

		private void DeserializeCurrent()
		{
			Rri = Deserialize(RockInfoString);
			if (Rri != null)
			{
				Texture texture = null;
				if (Rri.TextureIndex < 0 || Rri.TextureIndex > Rri.Data.Type.Textures.Length)
				{
					Log.Add("ERROR: RockPart Deserialize fail: texture index {0} is invalid for type {1} (there are {2} textures for that type). Loaded string: {3}",
						Rri.TextureIndex, Rri.Data.Type.Name, Rri.Data.Type.Textures.Length, RockInfoString);
					texture = Plexi.Tools.TextureManager.FailTexture;
				}
				else
					texture = Rri.Data.Type.Textures[Rri.TextureIndex];
				Renderer[] renderers = part.FindModelComponents<Renderer>();
				foreach (Renderer renderer in renderers)
					renderer.material.mainTexture = texture;
				part.mass = Rri.CalculateMass();
				part.transform.GetChild(0).localScale = new Vector3(Rri.Scale, Rri.Scale, Rri.Scale);
				part.partName = Rri.Data.Type.Name;
			}
		}
	}
}
